home *** CD-ROM | disk | FTP | other *** search
/ Acorn User: China / Acorn User China CD-ROM (UK) (Disc A) / Acorn User China CD-ROM (UK) (Disc A).bin / DEMON / MISC / NETLITE2.ARC / NET / c / FTPCLI < prev    next >
Encoding:
Text File  |  1993-04-25  |  31.7 KB  |  1,012 lines

  1. /* FTP client (interactive user) code */
  2. #define LINELEN         128     /* Length of command buffer */
  3. #include <stdio.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include <time.h>
  7. #include <stdarg.h>
  8. #include "dbox.h" 
  9. #include "werr.h"
  10. #include "global.h"
  11. #include "mbuf.h"
  12. #include "netuser.h"
  13. #include "icmp.h"
  14. #include "timer.h"
  15. #include "tcp.h"
  16. #include "ftp.h"
  17. #include "session.h"
  18. #include "cmdparse.h"
  19. #include "misc.h"
  20. #include "domain.h"
  21.  
  22. static struct ftpcmds {
  23.        char *name;
  24.        int  (*func)();
  25.        int  argcmin;
  26.        char *argc_errmsg;
  27.        char *help_msg;
  28. };
  29.  
  30. extern char nospace[];
  31. extern char badhost[];
  32. static char prompt[] = "ftp> ";
  33. static char notsess[] = "Not an FTP session!\n";
  34. static char cantwrite[] = "Can't write %s\n";
  35. static char cantread[] = "Can't read %s\n";
  36. static struct session *current;
  37.  
  38. static int  ftp(int32, char *, char *, char *, struct session *);
  39. static int  ftp_error(char *, char *, struct session *);
  40. static int  ftpcmdparse(struct ftpcmds *, char *);
  41. static int  docd(int, char **);
  42. static int  docdup(int, char **);
  43. static int  domkdir(int, char **);
  44. static int  dormdir(int, char **);
  45. static int  doascii(int, char **);
  46. static int  dobinary(int, char **);
  47. static int  dohelp(int, char **);
  48. static int  dotype(int, char **);
  49. static int  doget(int, char **);
  50. static int  dopwd(int, char **);
  51. static int  dobell(int, char **);
  52. static int  dodelete(int, char **);
  53. static int  dohash(int, char **);
  54. static int  dolist(int, char **);
  55. static int  dols(int, char **);
  56. static int  doput(int, char **);
  57. static int  doquit(int, char **);
  58. static int  doquote(int, char **);
  59. static int  douser(int, char **);
  60. static int  dopass(int, char **);
  61. static int  dorhelp(int, char **);
  62. static int  dosystem(int, char **);
  63. static void doreply(struct ftp *);
  64. static void ftpsetup(struct ftp *, void (*)(), void (*)(), void (*)());
  65. static void ftpccs(struct tcb *, char, char);
  66. static void ftpcds(struct tcb *, char, char);
  67. static int  sndftpmsg(struct ftp *, char *, ...); 
  68.  
  69. #define FTP_Connect        0
  70. #define FTP_Host           1
  71.  
  72. static struct ftpcmds ftpabort[] = {
  73.         {"abort",  doabort, 0, NULLCHAR, NULLCHAR},
  74.         {NULLCHAR, NULLFP,  0, NULLCHAR, NULLCHAR}
  75. };
  76.  
  77. static struct ftpcmds ftpcmds[] = {
  78.         {"ascii",   doascii,  0, NULLCHAR,               "set ascii transfer type"},
  79.         {"bell",    dobell,   0, NULLCHAR,               "beep when command completed"},
  80.         {"binary",  dobinary, 0, NULLCHAR,               "set binary transfer type"},
  81.         {"bye",     doquit,   0, NULLCHAR,               "terminate ftp session and quit"},
  82.         {"cd",      docd,     2, "cd <directory>\n",     "change remote working directory"},
  83.         {"cdup",    docdup,   0, NULLCHAR,               "change remote working directory to parent directory"},
  84.         {"delete",  dodelete, 2, "delete <remotefile>\n","delete remote file"},
  85.         {"dir",     dolist,   0, NULLCHAR,               "list contents of remote directory"},
  86.         {"get",     doget,    2, "get remotefile [localfile]\n","recive file"},
  87.         {"hash",    dohash,   0, NULLCHAR,               "toggle printing '#' for each buffer transferred"},
  88.         {"help",    dohelp,   0, NULLCHAR,               "print local help information"},
  89.         {"image",   dobinary, 0, NULLCHAR,               "set binary transfer mode"},
  90.         {"list",    dolist,   0, NULLCHAR,               "list contents of remote directory"},
  91.         {"ls",      dols,     0, NULLCHAR,               "nlist contents of remote directory"},
  92.         {"mkdir",   domkdir,  2, "mkdir <directory>\n",  "make directory on the remote machine"},
  93.         {"nlist",   dols,     0, NULLCHAR,               "nlist contents of remote directory"},
  94.         {"pass",    dopass,   2, "pass <password>\n",    "send password to remote system"},
  95.         {"put",     doput,    2, "put localfile [remotefile]\n","send one file"},
  96.         {"pwd",     dopwd,    0, NULLCHAR,               "print working directory on remote machine"},
  97.         {"quit",    doquit,   0, NULLCHAR,               "terminate ftp session and exit"},
  98.         {"quote",   doquote,  2, "quote <arguments>\n",  "send arbitrary ftp command"},
  99.         {"recv",    doget,    2, "recv remotefile [localfile]\n","receive file"},
  100.         {"remotehelp",dorhelp,0, NULLCHAR,               "get help from remote server"},
  101.         {"rhelp",   dorhelp,  0, NULLCHAR,               "get help from remote server"},
  102.         {"rmdir",   dormdir,  2, "rmdir <directory>\n",  "remove directory on the remote machine"},
  103.         {"send",    doput,    2, "send localfile [remotefile]\n","send one file"},
  104.         {"system",  dosystem, 0, NULLCHAR,               "show remote system type"},
  105.         {"type",    dotype,   0, NULLCHAR,               "set file transfer type"},
  106.         {"user",    douser,   2, "user <username>\n",    "send new user information"},
  107.         {"?",       dohelp,   0, NULLCHAR,               "print local help information"},
  108.         {NULLCHAR,   NULLFP,  0, NULLCHAR,               NULLCHAR}
  109. };
  110.  
  111. void start_ftp(void)
  112. {
  113.         struct session *session;
  114.         char buffer[80];
  115.         char Host[256];
  116.         dbox d;
  117.  
  118.         if ((d = dbox_new("FTP")) == NULL)
  119.                  return;
  120.  
  121.         dbox_setfield(d, FTP_Host, "");
  122.         dbox_show(d);
  123.  
  124.         if (dbox_fillin(d) == FTP_Connect)
  125.         {
  126.                  dbox_getfield(d, FTP_Host, Host, 255);
  127.  
  128.                  /* Allocate a session descriptor */
  129.                  sprintf(buffer, "FTP - %.60s", Host);
  130.                  if ((session = newsession(buffer)) == NULLSESSION)
  131.                  {
  132.                         werr(0, "Too many sessions");
  133.                         return;
  134.                  }
  135.  
  136.                  session->name  = strdup(Host);
  137.                  session->type  = RESOLVING;
  138.                  session->parse = NULLVFP;
  139.  
  140.                  sprintf(buffer, "Resolving %.60s ...\n", Host);
  141.                  Window_Write(session->window, buffer, strlen(buffer)); 
  142.  
  143.                  resolve_a(Host, NULLCHAR, NULLCHAR, session, ftp, ftp_error);
  144.         }
  145.  
  146.         dbox_dispose(&d);
  147. }
  148.  
  149. static int ftp_error(char *host, char *message, struct session *s)
  150. {
  151.         char buffer[80];
  152.  
  153.         sprintf(buffer, "Cannot FTP to %s - %s\n", host, message);
  154.         Window_Write(s->window, buffer, strlen(buffer));
  155.  
  156.         detachsession(s);
  157.  
  158.         return 0;  
  159. }
  160.  
  161. /* Handle top-level FTP command */
  162. static int ftp(int32 address, char *host, char *arg1, char *arg2, struct session *s)
  163. {
  164.         char buffer[80];
  165.         struct ftp *ftp;
  166.         struct tcb *tcb;
  167.         struct socket lsocket, fsocket;
  168.  
  169.         host = host;
  170.         arg1 = arg1;
  171.         arg2 = arg2;
  172.  
  173.         lsocket.address = ip_addr;
  174.         lsocket.port    = lport++;
  175.  
  176.         fsocket.address = address;
  177.         fsocket.port    = FTP_PORT;
  178.  
  179.         s->type  = FTP;
  180.         s->addr  = fsocket.address;
  181.         s->parse = ftpparse;
  182.         s->echo  = 1;
  183.         s->raw   = 0;
  184.  
  185.         /* Allocate an FTP control block */
  186.         if((ftp = ftp_create(LINELEN)) == NULLFTP){
  187.                 Window_Write(s->window, nospace, strlen(nospace));
  188.                 detachsession(s);
  189.                 return 1;
  190.         }
  191.         ftp->state = STARTUP_STATE;
  192.         ftp->hash  = 0;
  193.         ftp->bell  = 0;
  194.         s->cb.ftp  = ftp;       /* Downward link */
  195.         ftp->session = s;       /* Upward link */
  196.  
  197.         sprintf(buffer, "Trying [%s] ...\n", inet_ntoa(address));
  198.         Window_Write(s->window, buffer, strlen(buffer));
  199.  
  200.         /* Now open the control connection */
  201.         tcb = open_tcp(&lsocket,&fsocket,TCP_ACTIVE,
  202.                 0,(void(*)())ftpccr,NULLVFP,(void(*)())ftpccs,0,(char *)ftp);
  203.  
  204.         ftp->control = tcb;
  205.  
  206.         return 0;
  207. }
  208. /* Parse user FTP commands */
  209. void ftpparse(struct session *session, char *line, int16 len)
  210. {
  211.         char buffer[80];
  212.  
  213.         line[len] = '\0';
  214.         current = session;
  215.  
  216.         switch(session->cb.ftp->state){
  217.         case RECEIVING_STATE:
  218.         case SENDING_FILE_STATE:
  219.                 /* The only command allowed in data transfer state is ABORT */
  220.                 if (ftpcmdparse(ftpabort, line) == -1) {
  221.                         Window_Write(session->window, "Transfer in progress; only ABORT is acceptable\n", 47);
  222.                 }
  223.                 break;
  224.         case COMMAND_STATE:
  225.                 if (ftpcmdparse(ftpcmds, line) == -1) {
  226.                         Window_Write(session->window, "?Invalid command\n", 17);
  227.                         Window_Write(session->window, prompt, strlen(prompt));
  228.                 }
  229.                 break;
  230.         case STARTUP_STATE:             /* Startup up autologin */
  231.                 sprintf(buffer,"Not connected yet, ignoring %s\n",line);
  232.                 Window_Write(session->window, buffer, strlen(buffer));
  233.                 break;
  234.         case USER_STATE:                /* Got the user name */
  235.                 line[len] = '\0';
  236.                 sndftpmsg(session->cb.ftp,"USER %s",line);
  237.                 break;
  238.         case PASS_STATE:                /* Got the password */
  239.                 session->raw = 0;
  240.                 line[len] = '\0';
  241.                 sndftpmsg(session->cb.ftp,"PASS %s",line);
  242.                 break;
  243.         } 
  244. }
  245.  
  246. static int ftpcmdparse(struct ftpcmds *cmds, char *command)
  247. {
  248.         struct ftpcmds *cmd;
  249.         int argc = 0;
  250.         char *argv[NARG];
  251.         char *s;
  252.  
  253.         s = strtok(command, " \t\n\r");
  254.  
  255.         while (s != NULLCHAR && argc < NARG)
  256.         {
  257.                 argv[argc++] = s;
  258.                 s = strtok(NULLCHAR, " \t\n\r");
  259.         }
  260.  
  261.         if (argc == 0)
  262.         {
  263.                 Window_Write(current->window,prompt,strlen(prompt));
  264.                 return 0;
  265.         }
  266.  
  267.         for (cmd = cmds; cmd->name != NULLCHAR; cmd++)
  268.         {
  269.                 if (strncmp(cmd->name, argv[0], strlen(argv[0])) == 0)
  270.                 {
  271.                          if (argc < cmd->argcmin)
  272.                          {
  273.                                  Window_Write(current->window,cmd->argc_errmsg,strlen(cmd->argc_errmsg));
  274.                                  Window_Write(current->window,prompt,strlen(prompt));
  275.                                  return 0;
  276.                          }
  277.                          else
  278.                          {
  279.                                  (*cmd->func)(argc, argv);
  280.                                  return 0;
  281.                          }
  282.                 }
  283.         }
  284.  
  285.         return -1;
  286. }
  287. /* Translate 'cd' to 'cwd' for convenience */
  288. static int docd(int argc, char **argv)
  289. {
  290.         register struct ftp *ftp;
  291.  
  292.         argc = argc;
  293.  
  294.         ftp = current->cb.ftp;
  295.  
  296.         return sndftpmsg(ftp,"CWD %s\r\n",argv[1]);
  297. }
  298. /* Translate 'cd' to 'cwd' for convenience */
  299. static int docdup(int argc, char **argv)
  300. {
  301.         register struct ftp *ftp;
  302.  
  303.         argc = argc;
  304.         argv = argv;
  305.  
  306.         ftp = current->cb.ftp;
  307.  
  308.         return sndftpmsg(ftp,"CDUP\r\n");
  309. }
  310. /* Translate 'delete' to 'dele' for convenience */
  311. static int dodelete(int argc, char **argv)
  312. {
  313.         register struct ftp *ftp;
  314.  
  315.         argc = argc;
  316.  
  317.         ftp = current->cb.ftp;
  318.  
  319.         return sndftpmsg(ftp,"DELE %s\r\n",argv[1]);
  320. }
  321. /* Translate 'pwd' to 'pwd' for convenience */
  322. static int dopwd(int argc, char **argv)
  323. {
  324.         register struct ftp *ftp;
  325.  
  326.         argc = argc;
  327.         argv = argv;
  328.  
  329.         ftp = current->cb.ftp;
  330.  
  331.         return sndftpmsg(ftp,"PWD\r\n");
  332. }
  333. /* Translate 'rmdir' to 'rmd' for convenience */
  334. static int dormdir(int argc, char **argv)
  335. {
  336.         register struct ftp *ftp;
  337.  
  338.         argc = argc;
  339.  
  340.         ftp = current->cb.ftp;
  341.  
  342.         return sndftpmsg(ftp,"RMD %s\r\n",argv[1]);
  343. }
  344. /* Translate 'mkdir' to 'mkd' for convenience */
  345. static int domkdir(int argc, char **argv)
  346. {
  347.         register struct ftp *ftp;
  348.  
  349.         argc = argc;
  350.  
  351.         ftp = current->cb.ftp;
  352.  
  353.         return sndftpmsg(ftp,"MKD %s\r\n",argv[1]);
  354. }
  355. static int dobinary(int argc, char **argv)
  356. {
  357.         register struct ftp *ftp;
  358.  
  359.         argc = argc;
  360.         argv = argv;
  361.  
  362.         ftp = current->cb.ftp;
  363.  
  364.         ftp->type = IMAGE_TYPE;
  365.         sndftpmsg(ftp,"TYPE I\r\n");
  366.  
  367.         return(0);
  368. }
  369. static int doascii(int argc, char **argv)
  370. {
  371.         register struct ftp *ftp;
  372.  
  373.         argc = argc;
  374.         argv = argv;
  375.  
  376.         ftp = current->cb.ftp;
  377.         ftp->type = ASCII_TYPE;
  378.         sndftpmsg(ftp,"TYPE A\r\n");
  379.  
  380.         return(0);
  381. }
  382. static int dorhelp(int argc, char **argv)
  383. {
  384.         register struct ftp *ftp;
  385.  
  386.         argc = argc;
  387.         argv = argv;
  388.  
  389.         ftp = current->cb.ftp;
  390.  
  391.         sndftpmsg(ftp,"HELP\r\n");
  392.  
  393.         return(0);
  394. }
  395. static int dosystem(int argc, char **argv)
  396. {
  397.         register struct ftp *ftp;
  398.  
  399.         argc = argc;
  400.         argv = argv;
  401.  
  402.         ftp = current->cb.ftp;
  403.  
  404.         return sndftpmsg(ftp,"SYST\r\n");
  405. }
  406. static int doquote(int argc, char **argv)
  407. {
  408.         char buffer[80];
  409.         register struct ftp *ftp;
  410.         int i;
  411.  
  412.         ftp = current->cb.ftp;
  413.  
  414.         *buffer = '\0';
  415.         for (i = 1; i < argc; i++)
  416.         {
  417.               strcat(buffer, argv[i]);
  418.               strcat(buffer, " ");
  419.         }
  420.  
  421.         strcat(buffer, "\r\n");
  422.  
  423.         return sndftpmsg(ftp, buffer);
  424. }
  425. static int dohash(int argc, char **argv)
  426. {
  427.         register struct ftp *ftp;
  428.  
  429.         argc = argc;
  430.         argv = argv;
  431.  
  432.         ftp = current->cb.ftp;
  433.  
  434.         ftp->hash = !ftp->hash;
  435.  
  436.         if (ftp->hash)
  437.                 Window_Write(ftp->session->window,"Hash mark printing on (1024 bytes/hash mark).\n",46);
  438.         else
  439.                 Window_Write(ftp->session->window,"Hash mark printing off\n",23);
  440.  
  441.         Window_Write(ftp->session->window, prompt, strlen(prompt));
  442.  
  443.         return(0);
  444. }
  445.  
  446. static int dobell(int argc, char **argv)
  447. {
  448.         register struct ftp *ftp;
  449.  
  450.         argc = argc;
  451.         argv = argv;
  452.  
  453.         ftp = current->cb.ftp;
  454.  
  455.         ftp->bell = !ftp->bell;
  456.  
  457.         if (ftp->bell)
  458.                 Window_Write(ftp->session->window,"Bell mode on\n",13);
  459.         else
  460.                 Window_Write(ftp->session->window,"Bell mode off\n",14);
  461.  
  462.         Window_Write(ftp->session->window, prompt, strlen(prompt));
  463.  
  464.         return(0);
  465. }
  466.  
  467. static int dohelp(int argc, char **argv)
  468. {
  469.         char buffer[80];
  470.         struct ftpcmds *cmd;
  471.         struct ftp *ftp;
  472.         int n;
  473.  
  474.         ftp = current->cb.ftp;
  475.  
  476.         if (argc == 1)
  477.         {
  478.                 Window_Write(ftp->session->window, "Commands may be abbreviated. Commands are:\n", 43);
  479.  
  480.                 for (n = 0, cmd = ftpcmds; cmd->name != NULLCHAR; cmd++, n++)
  481.                 {
  482.                          sprintf(buffer, "%c%-13s", (n % 5) ? ' ' : '\n', cmd->name);
  483.                          Window_Write(ftp->session->window,buffer,strlen(buffer));
  484.                 }
  485.  
  486.                 Window_Write(ftp->session->window, "\n", 1);
  487.                 Window_Write(ftp->session->window, prompt, strlen(prompt));
  488.         }
  489.         else
  490.         {
  491.                 for (cmd = ftpcmds; cmd->name != NULLCHAR; cmd++)
  492.                 {
  493.                          if (strncmp(cmd->name, argv[1], strlen(argv[1])) == 0)
  494.                          {
  495.                                    sprintf(buffer, "%-13s%s\n",cmd->name,cmd->help_msg);
  496.                                    Window_Write(ftp->session->window,buffer,strlen(buffer));
  497.                                    Window_Write(ftp->session->window, prompt, strlen(prompt));
  498.                                    return 0;
  499.                          }
  500.                 }
  501.  
  502.                 sprintf(buffer, "?Invalid help command %s\n", argv[1]);
  503.                 Window_Write(ftp->session->window,buffer,strlen(buffer));
  504.                 Window_Write(ftp->session->window, prompt, strlen(prompt));
  505.         }
  506.  
  507.         return 0;
  508. }
  509.  
  510. static int douser(int argc, char **argv)
  511. {
  512.         register struct ftp *ftp;
  513.  
  514.         argc = argc;
  515.  
  516.         ftp = current->cb.ftp;
  517.  
  518.         return sndftpmsg(ftp,"USER %s\r\n",argv[1]);
  519. }
  520.         
  521. static int dopass(int argc, char **argv)
  522. {
  523.         register struct ftp *ftp;
  524.  
  525.         argc = argc;
  526.  
  527.         ftp = current->cb.ftp;
  528.  
  529.         return sndftpmsg(ftp,"PASS %s\r\n",argv[1]);
  530. }
  531.  
  532. /* Handle "type" command from user */
  533. static int dotype(int argc, char **argv)
  534. {
  535.         char buffer[100];
  536.         register struct ftp *ftp;
  537.  
  538.         ftp = current->cb.ftp;
  539.         if(argc < 2){
  540.                 switch(ftp->type){
  541.                 case IMAGE_TYPE:
  542.                         Window_Write(ftp->session->window,"Using binary mode to transfer files.\n",37);
  543.                         break;
  544.                 case ASCII_TYPE:
  545.                         Window_Write(ftp->session->window,"Using ascii mode to transfer files.\n",36);
  546.                         break;
  547.                 }
  548.  
  549.                 Window_Write(ftp->session->window, prompt, strlen(prompt));
  550.                 return 0;
  551.         }
  552.         switch(*argv[1]){
  553.         case 'i':
  554.         case 'b':
  555.                 ftp->type = IMAGE_TYPE;
  556.                 sndftpmsg(ftp,"TYPE I\r\n");
  557.                 break;
  558.         case 'a':
  559.                 ftp->type = ASCII_TYPE;
  560.                 sndftpmsg(ftp,"TYPE A\r\n");
  561.                 break;
  562.         case 'l':
  563.                 ftp->type = IMAGE_TYPE;
  564.                 sndftpmsg(ftp,"TYPE L %s\r\n",argv[2]);
  565.                 break;
  566.         default:
  567.                 sprintf(buffer,"%s: unknown mode\n",argv[1]);
  568.                 Window_Write(ftp->session->window,buffer,strlen(buffer));
  569.                 Window_Write(ftp->session->window, prompt, strlen(prompt));
  570.                 return 1;
  571.         }
  572.  
  573.         return 0;
  574. }
  575. /* Start receive transfer. Syntax: get <remote name> [<local name>] */
  576. static int doget(int argc, char **argv)
  577. {
  578.         char buffer[100];
  579.         char *remotename,*localname;
  580.         register struct ftp *ftp;
  581.         char *mode;
  582.  
  583.         ftp = current->cb.ftp;
  584.         if(ftp == NULLFTP){
  585.                 werr(0,notsess);
  586.                 return 1;
  587.         }
  588.         remotename = argv[1];
  589.         if(argc < 3)
  590.                 localname = remotename;
  591.         else
  592.                 localname = argv[2];
  593.  
  594.         if(ftp->fp != NULLFILE && ftp->fp != stdout)
  595.                 fclose(ftp->fp);
  596.         ftp->fp = NULLFILE;
  597.  
  598.         if(ftp->type == IMAGE_TYPE)
  599.                 mode = "wb";
  600.         else
  601.                 mode = "w";
  602.  
  603.         if((ftp->fp = fopen(localname,mode)) == NULLFILE){
  604.                 sprintf(buffer,cantwrite,localname);
  605.                 Window_Write(ftp->session->window,buffer,strlen(buffer));
  606.                 return 1;
  607.         }
  608.         time(&ftp->start);
  609.         ftp->bytes = 0;
  610.         ftp->state = RECEIVING_STATE;
  611.         ftpsetup(ftp,(void(*)())ftpdr,NULLVFP,(void(*)())ftpcds);
  612.  
  613.         /* Generate the command to start the transfer */
  614.         return sndftpmsg(ftp,"RETR %s\r\n",remotename);
  615. }
  616. /* List remote directory. Syntax: dir <remote directory/file> [<local name>] */
  617. static int dolist(int argc, char **argv)
  618. {
  619.         char buffer[100];
  620.         register struct ftp *ftp;
  621.  
  622.         ftp = current->cb.ftp;
  623.         if(ftp == NULLFTP){
  624.                 werr(0,notsess);
  625.                 return 1;
  626.         }
  627.         if(ftp->fp != NULLFILE && ftp->fp != stdout)
  628.                 fclose(ftp->fp);
  629.         ftp->fp = NULLFILE;
  630.  
  631.         if(argc < 3){
  632.                 ftp->fp = stdout;
  633.         } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
  634.                 sprintf(buffer,cantwrite,argv[2]);
  635.                 Window_Write(ftp->session->window,buffer,strlen(buffer));
  636.                 return 1;
  637.         }
  638.         time(&ftp->start);
  639.         ftp->bytes = 0;
  640.         ftp->state = RECEIVING_STATE;
  641.         ftpsetup(ftp,(void(*)())ftpdr,NULLVFP,(void(*)())ftpcds);
  642.         /* Generate the command to start the transfer
  643.          * It's done this way to avoid confusing the 4.2 FTP server
  644.          * if there's no argument
  645.          */
  646.         if (argc > 1)
  647.                 return sndftpmsg(ftp,"LIST %s\r\n",argv[1]);
  648.         else
  649.                 return sndftpmsg(ftp,"LIST\r\n","");
  650. }
  651. /* Abbreviated (name only) list of remote directory.
  652.  * Syntax: ls <remote directory/file> [<local name>]
  653.  */
  654. static int dols(int argc, char **argv)
  655. {
  656.         char buffer[100];
  657.         register struct ftp *ftp;
  658.  
  659.         ftp = current->cb.ftp;
  660.         if(ftp == NULLFTP){
  661.                 werr(0,notsess);
  662.                 return 1;
  663.         }
  664.         if(ftp->fp != NULLFILE && ftp->fp != stdout)
  665.                 fclose(ftp->fp);
  666.         ftp->fp = NULLFILE;
  667.  
  668.         if(argc < 3){
  669.                 ftp->fp = stdout;
  670.         } else if((ftp->fp = fopen(argv[2],"w")) == NULLFILE){
  671.                 sprintf(buffer,cantwrite,argv[2]);
  672.                 Window_Write(ftp->session->window,buffer,strlen(buffer));
  673.                 return 1;
  674.         }
  675.         time(&ftp->start);
  676.         ftp->bytes = 0;
  677.         ftp->state = RECEIVING_STATE;
  678.         ftpsetup(ftp,(void(*)())ftpdr,NULLVFP,(void(*)())ftpcds);
  679.         /* Generate the command to start the transfer */
  680.         if(argc > 1)
  681.                 return sndftpmsg(ftp,"NLST %s\r\n",argv[1]);
  682.         else
  683.                 return sndftpmsg(ftp,"NLST\r\n","");
  684. }
  685. /* Start transmit. Syntax: put <local name> [<remote name>] */
  686. static int doput(int argc, char **argv)
  687. {
  688.         char buffer[100];
  689.         char *remotename,*localname;
  690.         char *mode;
  691.         struct ftp *ftp;
  692.  
  693.         if((ftp = current->cb.ftp) == NULLFTP){
  694.                 werr(0,notsess);
  695.                 return 1;
  696.         }
  697.         localname = argv[1];
  698.         if(argc < 3)
  699.                 remotename = localname;
  700.         else
  701.                 remotename = argv[2];
  702.  
  703.         if(ftp->fp != NULLFILE && ftp->fp != stdout)
  704.                 fclose(ftp->fp);
  705.  
  706.         if(ftp->type == IMAGE_TYPE)
  707.                 mode = "rb";
  708.         else
  709.                 mode = "r";
  710.  
  711.         if((ftp->fp = fopen(localname,mode)) == NULLFILE){
  712.                 sprintf(buffer,cantread,localname);
  713.                 Window_Write(ftp->session->window,buffer,strlen(buffer));
  714.                 return 1;
  715.         }
  716.         time(&ftp->start);
  717.         ftp->bytes = 0;
  718.         ftp->state = SENDING_FILE_STATE;
  719.         ftpsetup(ftp,NULLVFP,(void(*)())ftpdt,(void(*)())ftpcds);
  720.  
  721.         /* Generate the command to start the transfer */
  722.         return sndftpmsg(ftp,"STOR %s\r\n",remotename);
  723. }
  724. /* Translate 'bye' and 'quit' to 'quit' for convenience */
  725. static int doquit(int argc, char **argv)
  726. {
  727.         register struct ftp *ftp;
  728.  
  729.         argc = argc;
  730.         argv = argv;
  731.  
  732.         ftp = current->cb.ftp;
  733.  
  734.         return sndftpmsg(ftp,"QUIT\r\n");
  735. }
  736. /* Abort a GET or PUT operation in progress. Note: this will leave
  737.  * the partial file on the local or remote system
  738.  */
  739. int doabort(int argc, char **argv)
  740. {
  741.         register struct ftp *ftp;
  742.  
  743.         argc = argc;
  744.         argv = argv;
  745.  
  746.         ftp = current->cb.ftp;
  747.  
  748.         /* Close the local file */
  749.         if(ftp->fp != NULLFILE && ftp->fp != stdout)
  750.                 fclose(ftp->fp);
  751.         ftp->fp = NULLFILE;
  752.  
  753.         switch(ftp->state){
  754.         case SENDING_FILE_STATE:
  755.                 /* Send a premature EOF.
  756.                  * Unfortunately we can't just reset the connection
  757.                  * since the remote side might end up waiting forever
  758.                  * for us to send something.
  759.                  */
  760.                 close_tcp(ftp->data);
  761.                 Window_Write(ftp->session->window, "Put aborted\n", 12);
  762.                 break;
  763.         case RECEIVING_STATE:
  764.                 /* Just exterminate the data channel TCB; this will
  765.                  * generate a RST on the next data packet which will
  766.                  * abort the sender
  767.                  */
  768.                 del_tcp(ftp->data);
  769.                 ftp->data = NULLTCB;
  770.                 Window_Write(ftp->session->window, "Get aborted\n", 12);
  771.                 break;
  772.         }
  773.         ftp->state = COMMAND_STATE;
  774.         Window_Write(ftp->session->window, prompt, strlen(prompt));
  775.         return 0;
  776. }
  777. /* create data port, and send PORT message */
  778. static void ftpsetup(struct ftp *ftp, void (*recv)(),
  779.                      void (*send)(), void (*state)())
  780. {
  781.         struct socket lsocket;
  782.         struct mbuf *bp;
  783.  
  784.         lsocket.address = ip_addr;
  785.         lsocket.port = lport++;
  786.  
  787.         /* Compose and send PORT a,a,a,a,p,p message */
  788.  
  789.         if((bp = alloc_mbuf(35)) == NULLBUF){   /* 5 more than worst case */
  790.                 werr(0,nospace);
  791.                 return;
  792.         }
  793.         /* I know, this looks gross, but it works! */
  794.         sprintf(bp->data,"PORT %u,%u,%u,%u,%u,%u\r\n",
  795.                 hibyte(hiword(lsocket.address)),
  796.                 lobyte(hiword(lsocket.address)),
  797.                 hibyte(loword(lsocket.address)),
  798.                 lobyte(loword(lsocket.address)),
  799.                 hibyte(lsocket.port),
  800.                 lobyte(lsocket.port));
  801.         bp->cnt = strlen(bp->data);
  802.         send_tcp(ftp->control,bp);
  803.  
  804.         /* Post a listen on the data connection */
  805.         ftp->data = open_tcp(&lsocket,NULLSOCK,TCP_PASSIVE,0,
  806.                 recv,send,state,0,(char *)ftp);
  807. }
  808. /* FTP Client Control channel Receiver upcall routine */
  809. void ftpccr(register struct tcb *tcb, int16 cnt)
  810. {
  811.         struct mbuf *bp;
  812.         struct ftp *ftp;
  813.         char c;
  814.  
  815.         if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  816.                 /* Unknown connection; kill it */
  817.                 close_tcp(tcb);
  818.                 return;
  819.         }
  820.  
  821.         if(recv_tcp(tcb,&bp,cnt) > 0){
  822.                 while(pullup(&bp,&c,1) == 1){
  823.                         switch(c){
  824.                         case '\r':      /* Strip cr's */  
  825.                                 continue;
  826.                         case '\n':      /* Complete line; process it */ 
  827.                                 ftp->buf[ftp->cnt] = '\0';
  828.                                 doreply(ftp);
  829.                                 ftp->cnt = 0;
  830.                                 break;
  831.                         default:
  832.                                 if(ftp->cnt != LINELEN-1)
  833.                                         ftp->buf[ftp->cnt++] = c;
  834.                                 break;
  835.                         }
  836.                 }
  837.         }
  838. }
  839.  
  840. /* Process replies from the server */
  841. static void doreply(register struct ftp *ftp)
  842. {
  843.         char buffer[80];
  844.         char **s;
  845.  
  846.         sprintf(buffer, "%s\n", ftp->buf);
  847.         Window_Write(ftp->session->window, buffer, strlen(buffer));
  848.  
  849.         if(ftp->cnt < 3) return;
  850.         switch(ftp->state){
  851.         case SENDING_FILE_STATE:
  852.         case RECEIVING_STATE:
  853.                 if (ftp->buf[0] == '5') doabort(0, s);
  854.                 break;
  855.         case STARTUP_STATE:
  856.                 if (strncmp(ftp->buf, "220", 3) == 0){
  857.                         ftp->state = USER_STATE;
  858.                         Window_Write(ftp->session->window, "Enter user name: ", 17);
  859.                 } else {
  860.                         ftp->state = COMMAND_STATE;
  861.                 }
  862.                 break;
  863.         case USER_STATE:
  864.                 if (strncmp(ftp->buf, "331", 3) == 0){
  865.                         ftp->state = PASS_STATE;
  866.                         ftp->session->echo = 0;
  867.                         Window_Write(ftp->session->window, "Password: ", 10);
  868.                 } else {
  869.                         ftp->state = COMMAND_STATE;
  870.                 }
  871.                 break;
  872.         case PASS_STATE:
  873.                 ftp->session->echo = 1;
  874.                 ftp->state = COMMAND_STATE;
  875.                 Window_Write(ftp->session->window,prompt,strlen(prompt));
  876.                 break;
  877.         case COMMAND_STATE:
  878.                 if (ftp->buf[0] >= '1' && ftp->buf[0] <= '5' && ftp->buf[3] == ' ')
  879.                         Window_Write(ftp->session->window,prompt,strlen(prompt));
  880.                 break;
  881.         }
  882. }
  883.  
  884. /* FTP Client Control channel State change upcall routine */
  885. static void ftpccs(register struct tcb *tcb, char old, char new)
  886. {
  887.         char buffer[40];
  888.         struct ftp *ftp;
  889.         extern char *tcpstates[];
  890.         extern char *reasons[];
  891.         extern char *unreach[];
  892.         extern char *exceed[];
  893.  
  894.         old = old;
  895.  
  896.         /* Can't add a check for unknown connection here, it would loop
  897.          * on a close upcall! We're just careful later on.
  898.          */
  899.         ftp = (struct ftp *)tcb->user;
  900.  
  901.         switch(new){
  902.         case CLOSE_WAIT:
  903.                 sprintf(buffer,"%s\n",tcpstates[new]);
  904.                 Window_Write(ftp->session->window,buffer,strlen(buffer));
  905.                 close_tcp(tcb);
  906.                 break;
  907.         case CLOSED:    /* heh heh */
  908.                 sprintf(buffer,"%s (%s",tcpstates[new],reasons[tcb->reason]);
  909.                 Window_Write(ftp->session->window,buffer,strlen(buffer));
  910.                 if(tcb->reason == NETWORK){
  911.                         switch(tcb->type){
  912.                         case DEST_UNREACH:
  913.                                 sprintf(buffer,": %s unreachable",unreach[tcb->code]);
  914.                                 Window_Write(ftp->session->window,buffer,strlen(buffer));
  915.                                 break;
  916.                         case TIME_EXCEED:
  917.                                 sprintf(buffer,": %s time exceeded",exceed[tcb->code]);
  918.                                 Window_Write(ftp->session->window,buffer,strlen(buffer));
  919.                                 break;
  920.                         }
  921.                 }
  922.                 Window_Write(ftp->session->window,")\n",2);
  923.                 del_tcp(tcb);
  924.                 if(ftp != NULLFTP)
  925.                         ftp_delete(ftp);
  926.                 break;
  927.         default:
  928.                 sprintf(buffer,"%s\n",tcpstates[new]);
  929.                 Window_Write(ftp->session->window,buffer,strlen(buffer));
  930.                 break;
  931.         }
  932. }
  933. /* FTP Client Data channel State change upcall handler */
  934. static void ftpcds(struct tcb *tcb, char old, char new)
  935. {
  936.         char buffer[80];
  937.         struct ftp *ftp;
  938.         time_t timenow;
  939.         double elapsed;
  940.         double average;
  941.  
  942.         old = old;
  943.  
  944.  
  945.         if((ftp = (struct ftp *)tcb->user) == NULLFTP){
  946.                 /* Unknown connection, kill it */
  947.                 close_tcp(tcb);
  948.                 return;
  949.         }
  950.  
  951.         time(&timenow);
  952.         if ((elapsed = difftime(timenow, ftp->start)) == 0.0) elapsed = 1.0;
  953.         average = (float)ftp->bytes / elapsed;
  954.  
  955.         switch(new){
  956.         case FINWAIT2:
  957.         case TIME_WAIT:
  958.                 if(ftp->state == SENDING_FILE_STATE){
  959.                         /* We've received an ack of our FIN, so
  960.                          * return to command mode
  961.                          */
  962.                         ftp->state = COMMAND_STATE;
  963.                         if (ftp->hash) Window_Write(ftp->session->window,"\n",1);
  964.                         if (ftp->bell) Window_Write(ftp->session->window,"\007",1);
  965.                         sprintf(buffer,"Put complete, %d bytes sent in %.0f seconds (%.0f bytes/sec)\n",
  966.                                         ftp->bytes,elapsed,average);
  967.                         Window_Write(ftp->session->window,buffer,strlen(buffer));
  968.                 }
  969.                 break;          
  970.         case CLOSE_WAIT:
  971.                 close_tcp(tcb);
  972.                 if(ftp->state == RECEIVING_STATE){
  973.                         /* End of file received on incoming file */
  974.                         if(ftp->fp != stdout)
  975.                                 fclose(ftp->fp);
  976.                         ftp->fp = NULLFILE;
  977.                         ftp->state = COMMAND_STATE;
  978.                         if (ftp->hash) Window_Write(ftp->session->window,"\n",1);
  979.                         if (ftp->bell) Window_Write(ftp->session->window,"\007",1);
  980.                         sprintf(buffer,"Get complete, %d bytes received in %.0f seconds (%.0f bytes/sec)\n",
  981.                                         ftp->bytes,elapsed,average);
  982.                         Window_Write(ftp->session->window,buffer,strlen(buffer));
  983.                 }
  984.                 break;
  985.         case CLOSED:
  986.                 ftp->data = NULLTCB;
  987.                 del_tcp(tcb);
  988.                 break;
  989.         }
  990. }
  991. /* Send a message on the control channel */
  992. static int sndftpmsg(struct ftp *ftp, char *fmt, ...)
  993. {
  994.         va_list argptr;
  995.         struct mbuf *bp;
  996.         int16 len;
  997.  
  998.         va_start(argptr,fmt);
  999.         len = strlen(fmt) + strlen(va_arg(argptr,char *)) + 10;
  1000.         va_end(argptr);
  1001.         if((bp = alloc_mbuf(len)) == NULLBUF){
  1002.                 Window_Write(ftp->session->window, nospace, strlen(nospace));
  1003.                 return 1;
  1004.         }
  1005.         va_start(argptr,fmt);
  1006.         vsprintf(bp->data,fmt,argptr);
  1007.         va_end(argptr);
  1008.         bp->cnt = strlen(bp->data);
  1009.         send_tcp(ftp->control,bp);
  1010.         return 0;
  1011. }
  1012.